<?php
/**
*
* Copyright (c) 2009 James Gauld <james@jamesgauld.net>
* This file is part of Scribble.
* @license http://www.scribblecms.co.uk/license.txt
*
* @package Scribble
*/
use Buan\Model;
use Buan\ModelCollection;
use Buan\ModelCriteria;
use Buan\UrlCommand;
class PageVersionModel extends ScribbleVersionModel {

	/**
	* Returns this Page's "handlesubpaths" settings, which defines whether or not
	* it can handle missing sub paths.
	*
	* For example, if this Page resides on the URL:
	*		/my/page.html
	* And a request is made for a non-existent resource:
	* 	/my/page/and/something.html
	* Then the "/and/something.html" part will be passed through as extra action
	* parameters when rendering $this Page.
	*
	* @return bool
	*/
	public function handleSubPaths() {
		return $this->handlesubpaths==1;
	}

	/**
	* Return an instance of PageContent model associated with $this version.
	*
	* If no content is available, a new PageContent instance is created, attached
	* to $this page and returned. Note this instance will NOT have a template
	* specified so you must define this before saving.
	*
	* @return Buan\Model
	*/
	public function getInnerContent() {

		// Find the inner content model related to $this version
		$c = new ModelCriteria();
		$c->addClause(ModelCriteria::EQUALS, 'type', ScribbleTemplate::TYPE_INNER);
		$c->setRange(0, 1);
		$cm = $this->findRelatives('PageContent', $c)->asArray();

		// No content found; attach and return new instance
		if(empty($cm)) {
			$cm = Model::create('PageContent');
			$cm->addRelatives($this);
			$cm->type = ScribbleTemplate::TYPE_INNER;
			return $cm;
		}

		// Content found; return the instance
		else {
			// This commented line MAY be required if not creating a brand new
			// revision because if !$page->hasChanged() then the save routine for
			// $content won't be triggered when $page itself is saved.
			$cm = array_shift($cm);
			$cm->addRelatives($this);
			return $cm;

			//return array_shift($cm);
		}
	}

	/**
	* Calling this method is the recommended way of getting hold of the front-end
	* View of this version's inner template.
	*
	* @return ScribblePublicView
	* @throws Buan\ModelException if there's anything wrong with the template
	*/
	public function getInnerView() {
		try {
			$cm = $this->getInnerContent();
			$tpl = ScribbleTemplateManager::getTemplateByName($cm->template);
			return $tpl->getFrontEndView($this);
		}
		catch(ScribbleTemplateException $e) {
			throw new ModelException($e->getMessage());
		}
	}

	/**
	* Return an instance of PageContent model associated with $this version.
	*
	* If no content is available, a new PageContent instance is created, attached
	* to $this page and returned. Note this instance will NOT have a template
	* specified so you must define this before saving.
	*
	* @return Buan\Model
	*/
	public function getOuterContent() {

		// Find the outer content model related to $this version
		$c = new ModelCriteria();
		$c->addClause(ModelCriteria::EQUALS, 'type', ScribbleTemplate::TYPE_OUTER);
		$c->setRange(0, 1);
		$cm = $this->findRelatives('PageContent', $c)->asArray();

		// No content found; attach and return new instance
		if(empty($cm)) {
			$cm = Model::create('PageContent');
			$cm->addRelatives($this);
			$cm->type = ScribbleTemplate::TYPE_OUTER;
			return $cm;
		}

		// Content found; return the instance
		else {
			// This commented line MAY be required if not creating a brand new
			// revision because if !$page->hasChanged() then the save routine for
			// $content won't be triggered when $page itself is saved.
			$cm = array_shift($cm);
			$cm->addRelatives($this);
			return $cm;

			//return array_shift($cm);
		}
	}

	/**
	* Calling this method is the recommended way of getting hold of the front-end
	* View of this version's outer template.
	*
	* As well as generating the View, it will also do the following:
	* - Add an "X-Pingback" HTTP header, unless $this Page explicitly forbids it
	*
	* @return ScribblePublicView
	* @throws Buan\ModelException if there's anything wrong with the template
	*/
	public function getOuterView() {
		try {
			$cm = $this->getOuterContent();
			$tpl = ScribbleTemplateManager::getTemplateByName($cm->template);
			$view = $tpl->getFrontEndView($this);
			if(($site = $this->findMaster()->findRelatives('Site')->get(0))!==NULL) {
				$view->setHeader('X-Pingback', "http://{$site->prefix}".UrlCommand::createUrl('pingback'));
			}
			return $view;
		}
		catch(ScribbleTemplateException $e) {
			throw new ModelException($e->getMessage());
		}
	}

	/**
	* Return this Page's tags in a simple array.
	*
	* @return array
	*/
	public function getTagsArray() {
		$tagModels = $this->findRelatives('TagPage');
		$tags = array();
		foreach($tagModels as $tm) {
			$tags[] = $tm->tag;
		}
		return $tags;
	}

	/**
	* Intercept calls to "... = $this->targetmenus' to JSON-decode this Page's
	* list of target menus into an array.
	*
	* @return array
	*/
	public function getTargetmenus() {
		return !empty($this->dbData['targetmenus']) ? json_decode($this->dbData['targetmenus']) : array();
	}

	/**
	* Store the inner-template content for this Page.
	*
	* @param mixed An object
	* @return void
	*/
	public function setInnerContent($content) {
		$cm = $this->getInnerContent();
		$cm->content = json_encode($content);
		return $cm;

		// First, check if any changes to the content have actually taken place
		// otherwise create a new revision to store the content
		$encoded = json_encode($content);
		$cm = $this->getInnerContent();
		if($cm->isInDatabase() && json_encode($cm->content)==$encoded) {
			return $cm;
		}
		else if($cm->isInDatabase()) {
			$tpl = $cm->template;
			unset($cm);
			$cm = Model::create('PageContent');
			$cm->addRelatives($this);
			$cm->template = $tpl;
			$cm->type = ScribbleTemplate::TYPE_INNER;
		}
		$cm->content = json_encode($content);
		return $cm;
	}

	/**
	* Store the outer-template content for this Page.
	*
	* @param mixed An object
	* @return void
	*/
	public function setOuterContent($content) {
		$cm = $this->getOuterContent();
		$cm->content = json_encode($content);
		return $cm;

		// First, check if any changes to the content have actually taken place
		// otherwise create a new revision to store the content
		$encoded = json_encode($content);
		$cm = $this->getOuterContent();
		if($cm->isInDatabase() && json_encode($cm->content)==$encoded) {
			return $cm;
		}
		else if($cm->isInDatabase()) {
			$tpl = $cm->template;
			unset($cm);
			$cm = Model::create('PageContent');
			$cm->addRelatives($this);
			$cm->template = $tpl;
			$cm->type = ScribbleTemplate::TYPE_OUTER;
		}
		$cm->content = json_encode($content);
		return $cm;
	}

	/**
	* Convenience method for adding/removing tags associated with this Page.
	*
	* Any previously associated tags that are not listed in $tags will be returned
	* in a ModelCollection object so they can be handled as required (usually
	* deleted, or you may flag them in some way, etc).
	*
	* @param array List of tags to associate with this Page
	* @return ModelCollection
	*/
	public function setTagsArray($tags) {

		// Sanitize tags first so they're consistent across the system
		foreach($tags as $k=>$t) {
			$t = preg_replace("/[^a-z0-9\-\s]/", "", strtolower($t));
			$t = preg_replace("/^\s+/", "", preg_replace("/\s+$/", "", $t));
			$tags[$k] = $t;
		}

		// Load existing tags and remove any that are not in $tags
		$current = $this->findRelatives('TagPage');
		$disowned = array();
		foreach($current as $c) {
			if(!in_array($c->tag, $tags)) {
				$disowned[] = $c;
				$this->disownRelatives($c);
			}
			else {
				$this->addRelatives($c);
				$tags = array_diff($tags, array($c->tag));
			}
		}

		// Add all new tags
		foreach($tags as $t) {
			$m = Model::create('Tag');
			$m->tag = $t;
			$m->getModelManager()->load($m);
			$this->addRelatives($m);
		}

		// Return the list of disowned tags
		return new ModelCollection($disowned);
	}

	/**
	* Intercept calls to "$this->targetmenus = ...' to JSON-encode arrays.
	*
	* @param array|string Value
	* @return void
	*/
	public function setTargetmenus($val) {
		$this->dbData['targetmenus'] = is_array($val) ? json_encode($val) : $val;
	}
}
?>